//
// Created by vivi on 11/09/2017.
//

#include "HairBaseSimulator.h"

namespace XRwy {

	using namespace Eigen;
	using namespace std;

	void HairBaseSimulator::preprocess(const HairSimulatorData &data) {

	}

	HairBaseSimulator::HairBaseSimulator(HairBaseSimulatorConfiguration *conf_ptr_) {
		this->conf_ptr = conf_ptr_;

		if (this->conf_ptr->stiction_type == "cage")
			this->stiction_solver_ptr = new HairStictionCageSolver(*this->conf_ptr);
		else if (this->conf_ptr->stiction_type == "density")
			this->stiction_solver_ptr = new HairStictionDensityFieldSolver(*this->conf_ptr);
	}

	void HairBaseSimulator::on_frame(const HairSimulatorData &data) {
		data.sdf_ptr->update(data.sdf_world2local, data.delta_time);

		const int pass = conf_ptr->enable_npass ? conf_ptr->npass : 1;
		const float time = data.delta_time / pass;

		//calculate the delta transform
		Matrix4f delta_affine_matrix = (data.motion_transform.matrix() - previous_transform.matrix()) / pass;
		Affine3f current_affine = previous_transform;

		for (size_t i = 0; i < pass; ++i) {
			current_affine = Affine3f(current_affine.matrix() + delta_affine_matrix);

			strand_dynamic(current_affine, time);

			if (conf_ptr->enable_hair_stiction)
				on_stiction(data, current_affine, time);

			if (conf_ptr->enable_body_collision)
				on_collision(data, current_affine, time);
		}

		previous_transform = data.motion_transform;
	}

	void HairBaseSimulator::on_collision(const HairSimulatorData &data, const Eigen::Affine3f &transformation, const float t) {
		for (const auto & strand_view : strands) {
			for (size_t i = 0; i < strand_view.all_ids.size(); ++i) {
				//we don't need to correct the position of the hair root
				auto & particle_view = particles[strand_view.all_ids[i]];
				data.sdf_ptr->fix(particle_view.pos(), particle_view.vel(), t);
			}
		}
	}

	void HairBaseSimulator::on_stiction(const HairSimulatorData &data, const Eigen::Affine3f &transformation, const float t) {
		stiction_solver_ptr->solve(*this, data, transformation, t);
	}
}